#include <malloc.h>
#include <pthread.h>

#include "log.h"
#include "core_data.h"
#include "mem.h"

struct core_data_queue *core_data_queue_create(void)
{
	log_info("begin core_data_queue_create.");	
	struct core_data_queue *core_data_queue = NULL;
	core_data_queue = (struct core_data_queue *)malloc(sizeof(struct core_data_queue));
	if (core_data_queue == NULL) {
		log_error("No enough mem.");
		return NULL;
	}
	
	int err = 0;
	err = pthread_mutex_init(&(core_data_queue->lock), NULL);
	if (err != 0) {
		log_error("cann't init lock.");
		free(core_data_queue);
		return NULL;
	}

	core_data_queue->next_id = 0;
	core_data_queue->max_id = MAX_ID;
	core_data_queue->mode = MODE_DEFAULT; 
	core_data_queue->core_data_head = NULL;
	core_data_queue->core_data_tail = NULL;
	core_data_queue->count = 0;
	core_data_queue->freed = 0;
	core_data_queue->dont_free = 0;

	return core_data_queue;
}

static struct core_data *core_data_create(void)
{
	log_info("begin core_data_create.");

	/* Now, we use s_malloc instead of malloc, because this functon can only be called by a task which is already running. */
	struct core_data *core_data = (struct core_data *)s_malloc(sizeof(struct core_data));
	if (core_data == NULL) {
		log_error("No enough mem.");
		return NULL;
	}

	int err = 0;
	err = pthread_mutex_init(&(core_data->lock), NULL);
	if (err != 0) {
		log_error("cann't init lock.");
		s_free(core_data);
		return NULL;
	}

	core_data->id = 0;
	core_data->ref = 0;
	core_data->prev = NULL;
	core_data->next = NULL;
	core_data->data = NULL;
	core_data->packet_tag_head = NULL;
	core_data->ipsize = 0;
	core_data->rawsize = 0;

	return core_data;
}

/* 
 * Who calld this function should already get all locks,
 * core_data_queue->core_data_tail->prev is not NULL,
 * and should be sure thar core_data_queue is not NULL. 
 *  */
void remove_core_data_at_tail(struct core_data_queue *core_data_queue)
{
	log_info("begin remove_core_data_at_tail.");
	/* To save time, don't check core_data_queue != NULL */	
	core_data_queue->count--;
	core_data_queue->freed++;
	core_data_queue->core_data_tail = core_data_queue->core_data_tail->prev;	
}

int add_core_data(struct core_data_queue *core_data_queue, void *data, unsigned short ipsize, unsigned short rawsize)
{
	log_info("begin add_core_data.");	
	if (core_data_queue == NULL) {
		log_error("core_data_queue is NULL.");
		return -1;
	}

	if (data == NULL) {
		log_error("data is NULL.");
		return -2;
	}

	struct core_data *core_data = core_data_create();
	if (core_data == NULL) {
		log_error("core_data_create failed.");
		return -3;
	}

	core_data->data = data;
	core_data->ipsize = ipsize;
	core_data->rawsize = rawsize;
	pthread_mutex_lock(&(core_data_queue->lock));

	core_data->id = core_data_queue->next_id++;
	core_data->useless = 0;

	if (core_data_queue->mode == MODE_DEFAULT) {
		if ((core_data_queue->core_data_head != NULL && core_data_queue->core_data_tail != NULL) 
			&& (core_data_queue->core_data_head->id - core_data_queue->core_data_tail->id >= core_data_queue->max_id)) {
			core_data_queue->next_id--;	
			free(core_data);
		} else {
			core_data->data = data;
			if (core_data_queue->core_data_head == NULL) {
				core_data_queue->core_data_head = core_data;
				core_data_queue->core_data_tail = core_data;
			} else {
				core_data_queue->core_data_head->prev = core_data;	
				core_data->next = core_data_queue->core_data_head;
				core_data_queue->core_data_head = core_data;
			}
			core_data_queue->count++;
		}
	} else if (core_data_queue->mode == MODE_NOTHING){
		core_data_queue->next_id--;
		free(core_data);
	}

	pthread_mutex_unlock(&(core_data_queue->lock));

	return 0;

}

/*
 * Now, no one need core_data, so we needn't worry
 * about core_data->lock.
 * Remmenber: use s_free()
 * */
void core_data_free(struct core_data *core_data)
{
	log_info("begin core_data_free.");
	pthread_mutex_destroy(&(core_data->lock));	

	s_free(core_data->data);	
	struct packet_tag *packet_tag = core_data->packet_tag_head;
	while (packet_tag != NULL) {
		struct packet_tag *be_free = packet_tag;
		packet_tag = packet_tag->next;
		s_free(be_free);
	}
}
